﻿using Xejen.Communication.Configuration;
using Xejen.Communication.Socket.Abstract;
using Xejen.Communication.Socket.Configuration;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace Xejen.Communication.Socket
{
    /// <summary>
    /// 使用同步阻塞方式<see langword="Socket.Send"/>和<see langword="Socket.Receive"/>的技术向远程服务器发起请求
    /// </summary>
    /// <creator>marc</creator>
    public class SyncInvoker : ISocketInvoker
    {
        private readonly ConnectionConfig _config;

        private bool _disposed;
        private System.Net.Sockets.Socket _clientSocket;

        /// <inheritdoc cref="SyncInvoker"/>
        protected SyncInvoker() { }

        /// <inheritdoc cref="SyncInvoker"/>
        public SyncInvoker(ConnectionConfig config)
        {
            Check.NotNull(config, nameof(config));

            _config = config;

            if (config.ConnectMode == ConnectMode.Long)
            {
                _clientSocket = CreateSocket();
            }
        }

        /// <summary>
        /// 创建一个新的套接字
        /// </summary>
        /// <returns></returns>
        private System.Net.Sockets.Socket CreateSocket()
        {
            var clientSocket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            clientSocket.SendBufferSize = _config.SendBufferSize;
            clientSocket.ReceiveBufferSize = _config.ReceiveBufferSize;
            clientSocket.SendTimeout = _config.SendTimeout;
            clientSocket.ReceiveTimeout = _config.ReceiveTimeout;

            return clientSocket;
        }

        /// <inheritdoc/>
        public async Task<SocketResponse> SendAsync(byte[] input, int timeout = -1, CancellationToken cancellationToken = default)
        {
            return await InnertSendAsync(input, timeout, cancellationToken);
        }

        private async Task<SocketResponse> InnertSendAsync(byte[] input, int timeout = -1, CancellationToken cancellationToken = default)
        {
            _clientSocket = this._clientSocket ?? CreateSocket();

            var connectTask = Task.Run(() => _clientSocket.Connect(new IPEndPoint(IPAddress.Parse(_config.ServerAddress), _config.ServerPort)));

            var completedTask = await Task.WhenAny(connectTask, Task.Delay(timeout, cancellationToken));

            if (completedTask != connectTask)
            {
                throw new TimeoutException($"Socket连接超时，超时时间限定：{timeout} 毫秒");
            }

            await connectTask;

            byte[] dataBytes = input;// Config.Encoding.GetBytes(JsonSerializer.Serialize(input, JsonSerializerOptions));
            _clientSocket.Send(dataBytes);

            using (MemoryStream responseStream = new MemoryStream())
            {
                byte[] buffer = new byte[4096];

                while (true)
                {
                    if (_clientSocket.Poll(0, SelectMode.SelectRead))
                    {
                        int bytesRead = _clientSocket.Receive(buffer);
                        responseStream.Write(buffer, 0, bytesRead);

                        if (bytesRead < buffer.Length)
                        {
                            break;
                        }
                    }

                    if (cancellationToken.IsCancellationRequested)
                    {
                        throw new OperationCanceledException("Socket操作被取消");
                    }

                    // Sleep briefly to avoid high CPU usage in the loop
                    await Task.Delay(10);
                }

                SocketResponse result = SocketResponse.Success(responseStream.ToArray(), encoding: _config.Encoding);

                if (_config.ConnectMode == ConnectMode.Short)
                {
                    Close();
                }

                return result;
            }
        }

        /// <inheritdoc/>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Close()
        {
            _clientSocket?.Close();
            _clientSocket?.Dispose();
            _clientSocket = null;
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }

            if (disposing)
            {
                Close();
            }

            _disposed = true;
        }
    }
}
